package com.yfp.client.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.*;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.binarywang.wxpay.bean.notify.WxPayPartnerNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayPartnerRefundNotifyV3Result;
import com.google.gson.Gson;
import com.hy.corecode.idgen.WFGIdGenerator;
import com.yfp.client.domain.*;
import com.yfp.client.domain.dto.*;
import com.yfp.client.domain.*;
import com.yfp.client.domain.dto.*;
import com.yfp.client.domain.vo.DetailVO;
import com.yfp.client.domain.vo.DeviceInfoVO;
import com.yfp.client.domain.vo.OrderCreateVO;
import com.yfp.client.domain.vo.OrderVO;
import com.yfp.client.enums.*;
import com.yfp.client.mapper.*;
import com.yfp.client.service.*;
import com.yfp.client.enums.ClientCouponStatusEnums;
import com.yfp.client.enums.CouponTypeEnum;
import com.yfp.client.enums.GoodsTypeEnum;
import com.yfp.client.enums.OrderStatus;
import com.yfp.client.mapper.*;
import com.yfp.client.service.*;
import com.yfp.client.util.RedisDistributedLock;
import com.yfp.client.util.Response;
import com.yfp.client.util.SecurityUtils;
import com.yfp.common.core.constant.CacheConstants;
import com.yfp.common.core.constant.MqttConstants;
import com.yfp.common.core.domain.R;
import com.yfp.common.core.enums.OrderTypeEnum;
import com.yfp.common.core.enums.PackageStatusEnums;
import com.yfp.common.core.enums.PayStatusEnum;
import com.yfp.common.core.enums.PayTypeEnum;
import com.yfp.common.core.exception.InnerAuthException;
import com.yfp.common.core.exception.ServiceException;
import com.yfp.common.core.utils.DateUtils;
import com.yfp.common.core.utils.GeoUtils;
import com.yfp.common.core.utils.StringUtils;
import com.yfp.common.redis.service.RedisService;
import com.yfp.common.redis.utils.RedisDelayQueueUtil;
import com.yfp.common.redis.utils.RedisLock;
import com.yfp.operation.api.RemoteOperationService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.yfp.common.core.constant.CacheConstants.MINAPP_ORDER_TAKE_QR_CODE_KEY;

/**
 * 
 * @version 1.0
 * @date 2024-05-24 11:01
 */
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderListService orderListService;
    @Resource
    private OrderListMapper orderListMapper;
    @Resource
    private MachineMapper machineMapper;
    @Resource
    private MealReplacementMapper mealReplacementMapper;
    @Resource
    private ComboService comboService;
    @Resource
    private OrderDetailMapper orderDetailMapper;
    @Resource
    private OrderComboService orderComboService;
    @Resource
    private OrderDetailService orderDetailService;
    @Resource
    private MachineService machineService;
    @Resource
    private MealReplacementService mealReplacementService;
    @Resource
    private WFGIdGenerator wFGIdGenerator;
    @Resource
    private RedisLock redisLock;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedisService redisService;
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private RedisDelayQueueUtil redisDelayQueueUtil;
    @Resource
    private OrderComboMapper orderComboMapper;
    @Resource
    private DeviceCurrentComboStockService deviceCurrentComboStockService;
    @Resource
    private ImgResourceService imgResourceService;
    @Resource
    private ShoppingCartsService shoppingCartsService;
    @Resource
    private PositionTypeMapper positionTypeMapper;
    @Resource
    private ClientService clientService;
    @Resource
    private AreaService areaService;
    @Resource
    private RouteService routeService;
    @Resource
    private PositionService positionService;
    @Resource
    private CompanyService companyService;
    @Resource
    private TagComboService tagComboService;
    @Resource
    private OrderMakeService orderMakeService;
    @Resource
    private PayService payService;
    @Resource
    private RemoteOperationService remoteOperationService;
    @Resource
    private MqttGateway mqttGateway;
    @Resource
    private ClientMachineFavoritesMapper clientMachineFavoritesMapper;
    @Resource
    private ClientCouponService clientCouponService;
    @Autowired
    private ClientCouponMapper clientCouponMapper;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private MarCouponComboMapper marCouponComboMapper;
    @Autowired
    private ClientIntegralService clientIntegralService;
    @Resource
    private EnterpriseOrderService enterpriseOrderService;
    @Resource
    private EnterpriseOrderClientMapper enterpriseOrderClientMapper;
    @Resource
    private OrderDetailWaitMapper orderDetailWaitMapper;
    @Resource
    private MarCouponService marCouponService;
    @Resource
    private OrderDetailWaitService orderDetailWaitService;
    @Resource
    private ClientBillService clientBillService;

    /**
     * 获取订单列表
     *
     * @param orderComboList
     * @param orderListDTO
     * @return
     */
    @Override
    public List<OrderVO> getAllOrder(List<OrderCombo> orderComboList, OrderListDTO orderListDTO) {
        // 检测订单是否过期
        this.checkUserOrderExpired();
        // 根据用户id获取数据
        if (CollUtil.isEmpty(orderComboList)) {
            return List.of();
        }
        // 获取要查询的订单id
        List<String> orderIdList = orderComboList.stream().map(OrderCombo::getOrderId).collect(Collectors.toList());
        // 获取设备id
        List<String> machineIds = orderComboList.stream().map(OrderCombo::getMachineId).distinct().collect(Collectors.toList());
        // 根据用户id获取收藏设备
        List<String> machineIdlist = clientMachineFavoritesMapper.getByClientId(orderListDTO.getUserId());
        // 根据设备id获取设备信息
        List<DeviceInfoVO> machineList = machineService.getByIds(machineIds);
        machineList.forEach(deviceInfoVO -> {
            String count = redisService.getCacheObject("temporaryLockers" + deviceInfoVO.getMachineId());
            if (StringUtils.isEmpty(count)) {
                count = "0";
            }
            deviceInfoVO.setTemporaryLockers(count);
            deviceInfoVO.setBookmark(false);
            if (!machineIdlist.isEmpty() && machineIdlist.contains(deviceInfoVO.getMachineId())) {
                deviceInfoVO.setBookmark(true);
            }
            Integer operationStatus = deviceInfoVO.getOperationStatus();
            if (operationStatus == 1) {
                deviceInfoVO.setBusinessStatus("运营中");
            } else if (operationStatus == 2) {
                deviceInfoVO.setBusinessStatus("休息中");
            } else if (operationStatus == 3) {
                deviceInfoVO.setBusinessStatus("保养中");
            } else if (operationStatus == 4) {
                deviceInfoVO.setBusinessStatus("保养中");
            }

            // 根据经纬度计算距离
            Double s = GeoUtils.calculateDistance(Double.parseDouble(deviceInfoVO.getLatitude()), Double.parseDouble(deviceInfoVO.getLongitude()), orderListDTO.getLatitude(), orderListDTO.getLongitude());
            String distance = String.valueOf((int) Math.ceil(s));
            deviceInfoVO.setDistanceCenterPoint(distance);
        });
        // 转换成设备map
        Map<String, DeviceInfoVO> deviceInfoVOMap = machineList.stream().collect(Collectors.toMap(DeviceInfoVO::getMachineId, Function.identity()));

        // 客服在线状态
        Map<String, Integer> onlineStatusMapByMachineId = mealReplacementService.operationalOnlineStatus(machineIds);

        // 获取订单相关联的详情数据
        LambdaQueryWrapper<OrderDetail> detailDOWrapper = new LambdaQueryWrapper<>();
        detailDOWrapper.in(OrderDetail::getOrderId, orderIdList);
        List<OrderDetail> detailDOList = orderDetailMapper.selectList(detailDOWrapper);
        List<OrderVO> collect = orderComboList.stream().map(item -> {
            OrderVO orderVO = new OrderVO();
            orderVO.setOrderId(item.getOrderId());
            orderVO.setUserId(item.getUserId());
            orderVO.setPrice(item.getPrice());
            orderVO.setStatus(item.getStatus());
            orderVO.setMachineId(item.getMachineId());
            orderVO.setMachineName(item.getMachineName());
            orderVO.setCreateTime(item.getCreateTime());
            orderVO.setReplacementState(item.getReplacementState());
            orderVO.setCupboardState(item.getCupboardState());
            orderVO.setReplacementState(item.getReplacementState());
            orderVO.setCupboardState(item.getCupboardState());
            // 获取客服在线状态
            Integer onlineStatus = MapUtil.getInt(onlineStatusMapByMachineId, item.getMachineId(), 0);
            orderVO.setOperationalOnlineStatus(onlineStatus);
            // 设置代取状态，为空则没点击过代取
            if (Objects.isNull(item.getReplacementState())) {
                orderVO.setReplacementState(0);
            }
            // 设置做好存柜状态，为空则没点击过做好存柜
            if (Objects.isNull(item.getCupboardState())) {
                orderVO.setCupboardState(0);
            }
            // 设置二维码状态，为空则没扫描过二维码
            CollUtil.edit(detailDOList, detailItem -> {
                if (detailItem.getScannedStatus() == null) {
                    detailItem.setScannedStatus(0);
                }
                return detailItem;
            });
            boolean b = detailDOList.stream().map(OrderDetail::getScannedStatus).allMatch(i -> i == 1);
            orderVO.setScannedStatus(b ? 1 : 0);

            List<OrderDetail> detailDOS = detailDOList.stream().filter(detailDO -> detailDO.getOrderId().equals(item.getOrderId())).collect(Collectors.toList());
            orderVO.setOrderDetailList(detailDOS);
            DeviceInfoVO deviceInfoVO = deviceInfoVOMap.get(item.getMachineId());
            if (!BeanUtil.isEmpty(deviceInfoVO)) {
                orderVO.setMachineType(deviceInfoVO.getMachineType());
                orderVO.setDeviceInfo(deviceInfoVO);
            }


            // 获取支付时间
            LambdaQueryWrapper<OrderList> queryWrapper = new LambdaQueryWrapper<OrderList>().eq(OrderList::getOrderSn, orderVO.getOrderId());
            List<OrderList> orderLists = orderListMapper.selectList(queryWrapper);

            if (!orderLists.isEmpty()) {
                OrderList first = CollUtil.getFirst(orderLists);
                String orderType = first.getOrderType();
                if (orderType != null) {
                    orderVO.setOrderType(first.getOrderType());
                }
                log.info("first: {}", first);
                if (null != first.getPayTime() && first.getOrderType().equals("2")) {
                    orderVO.setEndTime(DateUtils.addDay(first.getPayTime(), 1));

                    log.info("setEndTime: {}", orderVO.getEndTime());
                }
                orderVO.setGoodsType(first.getGoodsType());
            }


            return orderVO;
        }).collect(Collectors.toList());

        return collect;
    }

    /**
     * 获取订单详情
     *
     * @param orderId   订单id
     * @param latitude  纬度
     * @param longitude 经度
     * @return
     */
    @Override
    public DetailVO detail(String orderId, String latitude, String longitude) {

        // 获取订单信息
        OrderCombo orderCombo = orderComboService.getById(orderId);

        DetailVO detailVO = new DetailVO();

        // 默认值
        detailVO.setOperationalOnlineStatus(0);
        detailVO.setReplacementState(1);
        detailVO.setAwaitNum(0);
        // detailVO.setAwaitTime(0);

        detailVO.setOrder(orderCombo);
        String machineId = orderCombo.getMachineId();
        List<String> machineIds = new ArrayList<>();
        machineIds.add(machineId);
        // 根据设备id获取设备信息
        List<DeviceInfoVO> deviceInfoVOList = machineService.getByIds(machineIds);
        DeviceInfoVO deviceInfoVO = CollUtil.getFirst(deviceInfoVOList);
        if (Objects.nonNull(deviceInfoVO)) {
            String count = redisService.getCacheObject("temporaryLockers" + deviceInfoVO.getMachineId());
            if (StringUtils.isEmpty(count)) {
                count = "0";
            }
            deviceInfoVO.setTemporaryLockers(count);
            // 根据经纬度计算距离
            Double s = GeoUtils.calculateDistance(Double.parseDouble(deviceInfoVO.getLatitude()), Double.parseDouble(deviceInfoVO.getLongitude()), Double.parseDouble(latitude), Double.parseDouble(longitude));
            String distance = String.valueOf((int) Math.ceil(s));
            deviceInfoVO.setDistanceCenterPoint(distance);
            // 运营状态
            Integer operationStatus = deviceInfoVO.getOperationStatus();
            if (operationStatus == 1) {
                deviceInfoVO.setBusinessStatus("运营中");
            } else if (operationStatus == 2) {
                deviceInfoVO.setBusinessStatus("休息中");
            } else if (operationStatus == 3) {
                deviceInfoVO.setBusinessStatus("保养中");
            } else if (operationStatus == 4) {
                deviceInfoVO.setBusinessStatus("保养中");
            }
            detailVO.setDeviceInfo(deviceInfoVO);
        }

        // 获取支付时间
        LambdaQueryWrapper<OrderList> queryWrapper = new LambdaQueryWrapper<OrderList>().eq(OrderList::getOrderSn, orderId);
        List<OrderList> orderLists = orderListMapper.selectList(queryWrapper);
        OrderList first = CollUtil.getFirst(orderLists);

        // 客服状态
        Boolean status1 = mealReplacementService.getStatus(machineId);
        if (BooleanUtil.isTrue(status1)) {
            detailVO.setOperationalOnlineStatus(1);
        }
        // 客服手机号码和姓名
        DetailVO operational = orderListMapper.findOperational(first.getPositionId());
        if (null != operational) {
            detailVO.setOperationalName(operational.getOperationalName());
            detailVO.setOperationalMobile(operational.getOperationalMobile());
        }

        detailVO.setPayTime(first.getPayTime());
        detailVO.setOrderType(first.getOrderType());
        if (StrUtil.equals("0", detailVO.getOrderType()) && PayTypeEnum.BalancePay.getCode().toString().equals(first.getPayType())) {
            detailVO.setOrderType(PayTypeEnum.BalancePay.getCode().toString());
        }
        if (null != first.getPayTime() && first.getOrderType().equals("2")) {
            detailVO.setEndTime(DateUtils.addDay(first.getPayTime(), 1));
        }
        // 获取订单详情信息
        LambdaQueryWrapper<OrderDetail> detailLambdaQueryWrapper = new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderId);
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(detailLambdaQueryWrapper);
        detailVO.setOrderDetailList(orderDetails);
        boolean b = orderDetails.stream().map(OrderDetail::getScannedStatus).allMatch(i -> null != i && i == 1);
        detailVO.setScannedStatus(b ? 1 : 0);
        boolean a = orderDetails.stream().map(OrderDetail::getReplacementState).allMatch(i -> null != i && i == 1);
        detailVO.setReplacementState(a ? 1 : 0);
        if (machineId == null) {
            return detailVO;
        }
        List<String> orderDetailIds = orderDetails.stream().map(orderDetail -> orderDetail.getOrderDetailId()).toList();

        OrderDetailWait orderDetailWait = orderDetailWaitMapper.getCreateTime(orderDetailIds);
        if (Objects.nonNull(orderDetailWait)) {
            // 如果出口口为空，设置默认值A
            if (Objects.isNull(orderDetailWait.getOutMeal())) {
                orderDetailWait.setOutMeal("A");
            }
            // 获取等待时间列表
            List<OrderDetailWait> orderDetailWaits = orderDetailWaitMapper.waitTimes(orderDetailIds, machineId, orderDetailWait.getCreateTime());

            log.info("*****orderDetailWaits*****:{}",orderDetailWaits);

            // 如果出口口为空，设置默认值A
            orderDetailWaits.stream().filter(item -> Objects.isNull(item.getOutMeal())).forEach(item -> item.setOutMeal("A"));

            // 出餐口AB分租
            Map<String, List<OrderDetailWait>> collect = orderDetailWaits.stream().collect(Collectors.groupingBy(OrderDetailWait::getOutMeal));

            log.info("*****collect*****:{}",collect);

            LambdaQueryWrapper<OrderDetailWait> query = Wrappers.lambdaQuery(OrderDetailWait.class);
            query.eq(OrderDetailWait::getMachineId, machineId);
            query.in(OrderDetailWait::getOrderDetailId, orderDetailIds);
            List<String> strings = orderDetailWaitMapper.selectList(query).stream().map(OrderDetailWait::getOutMeal).distinct().toList();

            log.info("*****strings*****:{}",strings);


            HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();
            for (String outMeal : strings) {
                List<OrderDetailWait> orDefault = collect.getOrDefault(outMeal, new ArrayList<>(0));
                stringIntegerHashMap.put(outMeal,orDefault.size());
            }

            log.info("*****stringIntegerHashMap*****:{}",stringIntegerHashMap);

            Map.Entry<String, Integer> minEntry = stringIntegerHashMap.entrySet().stream()
                    .min(Comparator.comparingInt(Map.Entry::getValue))
                    .orElse(null);

            log.info("*****minEntry*****:{}",minEntry);

            // 获取第一个加入排队的出餐口列表
            detailVO.setAwaitNum(minEntry.getValue());
            // 查询出餐口列表
            List<OrderDetailWait> orderDetailWaits2 = orderDetailWaitMapper.getOutMealList(orderDetailIds, machineId);
            List<String> orderDetailWaitsOutMealList = orderDetailWaits2.stream().map(OrderDetailWait::getOutMeal).distinct().sorted().toList();
            detailVO.setOrderDetailWaitsOutMealList(orderDetailWaitsOutMealList);
        } else {
            Integer awaitNum = this.getAwaitNum(machineId);
            detailVO.setAwaitNum(awaitNum);
        }


        // 设置订单状态：[1：已下单、2：制作中、3：请取餐]
        boolean status1Bool = orderDetails.stream().map(OrderDetail::getStatus).allMatch(status -> {
            return PackageStatusEnums.WaitingToBeMade.getStatus().equals(status);
        });
        boolean status2Bool = orderDetails.stream().anyMatch(item -> {
            // 制作异常
            boolean b1 = PackageStatusEnums.MakeExceptions.getStatus().equals(item.getStatus()) && OrderStatus.AwaitPackage.getStatus().equals(orderCombo.getStatus());
            // 正在制作
            boolean b2 = PackageStatusEnums.InTheMaking.getStatus().equals(item.getStatus());
            // 已经加入排队队列，还没制作
            boolean b3 = false;
            if (Objects.nonNull(orderDetailWait)) {
                b3 = orderDetailWait.getStatus().equals(0);
            }
            boolean b4 = (item.getReplacementState() == 1 || item.getScannedStatus() == 1) && PackageStatusEnums.WaitingToBeMade.getStatus().equals(item.getStatus());
            return b1 || b2 || b3 || b4;
        });
        boolean status3Bool = orderDetails.stream().map(OrderDetail::getStatus).allMatch(status -> {
            return PackageStatusEnums.MadeDone.getStatus().equals(status) || PackageStatusEnums.PickupIsComplete.getStatus().equals(status);
        });
        if (status1Bool) {
            detailVO.setOrderStatus(1);
        } else if (status2Bool) {
            detailVO.setOrderStatus(2);
        } else if (status3Bool) {
            detailVO.setOrderStatus(3);
        } else {
            detailVO.setOrderStatus(0);
        }
        String orderDetailStatusList = orderDetails.stream().map(OrderDetail::getStatus).collect(Collectors.joining(","));
        log.info("detail状态:orderId:{}, orderStatus:{}, orderCombo.getStatus():{}, orderDetails:{}", orderId, detailVO.getOrderStatus(), orderCombo.getStatus(), orderDetailStatusList);
        return detailVO;
    }


    public static void main(String[] args) {


//        Map<String, List<OrderDetailWait>> collect = new HashMap<>();

        ArrayList<OrderDetailWait> list = new ArrayList<>();

        OrderDetailWait orderDetail = new OrderDetailWait();
        list.add(orderDetail);

//        collect.put("A", list);

//        List<OrderDetailWait> orDefault = collect.getOrDefault("B", new ArrayList<>(0));
//        System.out.println(orDefault);


        List<OrderDetailWait> orderDetailWaits = new ArrayList<>();

        Map<String, List<OrderDetailWait>> collect = orderDetailWaits.stream().collect(Collectors.groupingBy(OrderDetailWait::getOutMeal));

        LambdaQueryWrapper<OrderDetailWait> query = Wrappers.lambdaQuery(OrderDetailWait.class);


        List<String> strings = new ArrayList<>();
        strings.add("A");

        HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();
        for (String outMeal : strings) {
            List<OrderDetailWait> orDefault = collect.getOrDefault(outMeal, new ArrayList<>(0));
            stringIntegerHashMap.put(outMeal,orDefault.size());
        }

        Map.Entry<String, Integer> minEntry = stringIntegerHashMap.entrySet().stream()
                .min(Comparator.comparingInt(Map.Entry::getValue))
                .orElse(null);

        System.out.println(minEntry);





    }





    @Override
    public ClientCouponBestDTO settlementInfoOrder(OrderCreateDTO orderCreateDTO) {

        // 获取餐品价格
        List<String> packageId = orderCreateDTO.getShoppingCartList().stream().map(ShoppingCartsDto::getPackageId).collect(Collectors.toList());
        Map<String, Combo> comboMapByComboId = comboService.listByPackageIds(packageId).stream().collect(Collectors.toMap(Combo::getComboId, Function.identity()));
        orderCreateDTO.getShoppingCartList().forEach(item -> {
            Combo combo = comboMapByComboId.getOrDefault(item.getPackageId(), null);
            Assert.notNull(combo, "套餐不存在，请重新结算");
            item.setPrice(new BigDecimal(combo.getSalePrice()));
        });

        String clientId = null;
        try {
            clientId = SecurityUtils.getClientId();
        } catch (InnerAuthException e) {
            log.info("settlementInfoOrder,用户未登录时调用");
        }

        // 当用户没有选择优惠券时，计算最优的优惠券
        if (Objects.isNull(orderCreateDTO.getClientCouponId())) {
            return getClientCouponBestDTO(orderCreateDTO, clientId, comboMapByComboId);
        } else {
            // 当用户选择优惠券时，计算优惠券
            ClientCoupon clientCoupon = clientCouponService.findByIdAndClientId(orderCreateDTO.getClientCouponId(), clientId);
            Assert.isFalse(Objects.isNull(clientCoupon), "无此优惠券");
            List<ClientCoupon> availableCoupons = clientCouponService.getAvailableCoupons(orderCreateDTO, Collections.singletonList(clientCoupon));
            Assert.isFalse(CollUtil.isEmpty(availableCoupons), "此优惠券已不满足使用条件");

            // 填充霸王免单券的 discounts 优惠内容
            clientCoupon = CollUtil.getFirst(clientCouponService.fillBabyCouponDiscounts(orderCreateDTO, Collections.singletonList(clientCoupon)));

            if (0 == clientCoupon.getCombo()) {
                // 全部套餐可用
                // 订单总价，优惠前
                BigDecimal totalPrice = orderCreateDTO.getShoppingCartList().stream().map(item -> {
                    Combo combo = comboMapByComboId.getOrDefault(item.getPackageId(), null);
                    Assert.notNull(combo, "套餐不存在，请重新结算");
                    // 计算套餐总价 购买数量*小程序商品价格
                    return NumberUtil.mul(item.getQuantity().toString(), combo.getSalePrice());
                }).reduce(BigDecimal.ZERO, BigDecimal::add);
                return clientCouponService.getBestClientCoupon(totalPrice, Collections.singletonList(clientCoupon));
            } else {
                // 指定套餐可用
                Long couponId = clientCoupon.getCouponId();
                Map<Long, List<MarCouponCombo>> marCouponComboMapByCouponId = marCouponComboMapper.selectByMarCouponIds(Collections.singletonList(couponId)).stream().collect(Collectors.groupingBy(MarCouponCombo::getMarCouponId));
                List<String> couponComboIds = marCouponComboMapByCouponId.getOrDefault(couponId, List.of()).stream().map(MarCouponCombo::getComboId).collect(Collectors.toList());
                Map<String, ShoppingCartsDto> shoppingCartsDtoMapByPackageId = orderCreateDTO.getShoppingCartList().stream().collect(Collectors.toMap(ShoppingCartsDto::getPackageId, Function.identity()));
                BigDecimal price = couponComboIds.stream().map(item2 -> {
                    ShoppingCartsDto orDefault = shoppingCartsDtoMapByPackageId.getOrDefault(item2, null);
                    if (Objects.nonNull(orDefault)) {
                        // 计算套餐总价 购买数量*小程序商品价格
                        return NumberUtil.mul(orDefault.getQuantity(), orDefault.getPrice());
                    }
                    return BigDecimal.ZERO;
                }).reduce(BigDecimal.ZERO, BigDecimal::add);
                return clientCouponService.getBestClientCoupon(price, Collections.singletonList(clientCoupon));
            }
        }
    }

    @Override
    public ClientCouponBestDTO getClientCouponBestDTO(OrderCreateDTO orderCreateDTO, String clientId, Map<String, Combo> comboMapByComboId) {
        List<ClientCoupon> clientCouponList = null;
        log.info("getClientCouponBestDTO,clientId:{}", clientId);
        if (StrUtil.isBlank(clientId)) {
            // 未登录，无优惠券
            clientCouponList = new ArrayList<>();
        } else {
            // 使用设备城市
            Machine machine = machineService.findByMachineId(orderCreateDTO.getMachineId());
            // 用户所有的优惠券
            clientCouponList = clientCouponMapper.getClientCouponListByClientIdAndStatusType(clientId, ClientCouponStatusEnums.NORMAL.getCode(), machine.getCityName());
        }

        // 获取用户可使用的优惠券列表
        List<ClientCoupon> availableCoupons = clientCouponService.getAvailableCoupons(orderCreateDTO, clientCouponList);
        // 填充霸王免单券的 discounts 优惠内容
        availableCoupons = clientCouponService.fillBabyCouponDiscounts(orderCreateDTO, availableCoupons);

        // 订单总价，优惠前
        BigDecimal totalPrice = orderCreateDTO.getShoppingCartList().stream().map(item -> {
            Combo combo = comboMapByComboId.getOrDefault(item.getPackageId(), null);
            Assert.notNull(combo, "套餐不存在，请重新结算");
            // 计算套餐总价 购买数量*小程序商品价格
            return NumberUtil.mul(item.getQuantity().toString(), combo.getSalePrice());
        }).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 所有套餐均可用的券
        List<ClientCoupon> availableCombo0Coupons = availableCoupons.stream().filter(item -> 0 == item.getCombo()).collect(Collectors.toList());
        ClientCouponBestDTO bestClientCoupon1 = clientCouponService.getBestClientCoupon(totalPrice, availableCombo0Coupons);
        List<ClientCouponBestDTO> clientCouponBestDTOS = new ArrayList<>();
        clientCouponBestDTOS.add(bestClientCoupon1);
        // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        // 指定套餐可用的券
        List<ClientCoupon> availableCombo1Coupons = availableCoupons.stream().filter(item -> 1 == item.getCombo()).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(availableCombo1Coupons)) {
            List<Long> couponIds = availableCombo1Coupons.stream().map(ClientCoupon::getCouponId).collect(Collectors.toList());
            Map<Long, List<MarCouponCombo>> marCouponComboMapByCouponId = marCouponComboMapper.selectByMarCouponIds(couponIds).stream().collect(Collectors.groupingBy(MarCouponCombo::getMarCouponId));
            Map<String, ShoppingCartsDto> shoppingCartsDtoMapByPackageId = orderCreateDTO.getShoppingCartList().stream().collect(Collectors.toMap(ShoppingCartsDto::getPackageId, Function.identity()));
            List<ClientCouponBestDTO> bestClientCoupon2 = availableCombo1Coupons.stream().map(item -> {
                List<String> couponComboIds = marCouponComboMapByCouponId.getOrDefault(item.getCouponId(), List.of()).stream().map(MarCouponCombo::getComboId).collect(Collectors.toList());
                BigDecimal price = couponComboIds.stream().map(item2 -> {
                    ShoppingCartsDto orDefault = shoppingCartsDtoMapByPackageId.getOrDefault(item2, null);
                    if (Objects.nonNull(orDefault)) {
                        // 计算套餐总价 购买数量*小程序商品价格
                        return NumberUtil.mul(orDefault.getQuantity(), orDefault.getPrice());
                    }
                    return BigDecimal.ZERO;
                }).reduce(BigDecimal.ZERO, BigDecimal::add);
                return clientCouponService.getBestClientCoupon(price, Collections.singletonList(item));
            }).collect(Collectors.toList());
            clientCouponBestDTOS.addAll(bestClientCoupon2);
        }
        return clientCouponService.getBestSortClientCoupon(clientCouponBestDTOS.stream().filter(item -> Objects.nonNull(item.getId())).distinct().collect(Collectors.toList()));
    }

    /**
     * 创建订单
     *
     * @param orderCreateDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderCreateVO createOrder(OrderCreateDTO orderCreateDTO) {
        // String clientId = String.valueOf(5);
        String clientId = SecurityUtils.getClientId();
        log.info("createOrder,用户ID：{}", clientId);
        log.info("createOrder,orderCreateDTO:{}", orderCreateDTO);
        Assert.isTrue(CollUtil.isNotEmpty(orderCreateDTO.getShoppingCartList()), "购物车不能为空");
        orderCreateDTO.getShoppingCartList().stream().forEach(item -> {
            // 购买数量大于0校验
            Assert.isFalse(NumberUtil.isLess(NumberUtil.toBigDecimal(item.getQuantity()), BigDecimal.ONE), "购买数量不能小于0");
        });
        // 校验用户重复创建订单
        String isRepeat = redisService.getCacheObject(CacheConstants.MINAPP_ORDER_CREATE_REPEAT_KEY + orderCreateDTO.getMachineId() + ":" + clientId);
        Assert.isTrue(StringUtils.isEmpty(isRepeat), "请勿重复下单");
        // 校验设备是否在运营中
        Boolean businessOpenByMachineId = machineService.isBusinessOpenByMachineId(orderCreateDTO.getMachineId());
        Assert.isTrue(businessOpenByMachineId, "该设备不在营业时间");
        // 检查设备状态
        Machine machineDO = machineService.findByMachineId(orderCreateDTO.getMachineId());
        if (StrUtil.equals("0", machineDO.getOnlineStatus().toString())) {
            throw new ServiceException("设备不在线");
        }

        // 小程序下单锁
        String lockKey = CacheConstants.MINAPP_ORDER_CREATE_LOCK_KEY + orderCreateDTO.getMachineId();
        RLock lock = redissonClient.getLock(lockKey);
        Assert.isTrue(lock.tryLock(), "稍后重试");

        try {
            // 防止用户重复创建订单
            redisService.setCacheObject(CacheConstants.MINAPP_ORDER_CREATE_REPEAT_KEY + orderCreateDTO.getMachineId() + ":" + clientId, "true", 5L, TimeUnit.SECONDS);
            // 校验库存
            Boolean isStockAvailable = deviceCurrentComboStockService.checkStockAvailability(orderCreateDTO.getShoppingCartList(), orderCreateDTO.getMachineId());
            Assert.isTrue(isStockAvailable, () -> {
                OrderCreateVO orderCreateVO = new OrderCreateVO();
                orderCreateVO.setLackList(deviceCurrentComboStockService.lackList(orderCreateDTO.getShoppingCartList(), orderCreateDTO.getMachineId()));
                ServiceException serviceException = new ServiceException("库存不足");
                serviceException.setData(orderCreateVO);
                return serviceException;
            });

            // 封装订单主表数据
            Machine machine = machineService.findByMachineId(orderCreateDTO.getMachineId());
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            OrderCombo orderCombo = new OrderCombo();
            orderCombo.setOrderId(String.valueOf(wFGIdGenerator.next()));
            orderCombo.setMachineId(machine.getMachineId());
            orderCombo.setMachineName(machine.getMachineName());
            orderCombo.setUserId(clientId);
            orderCombo.setStatus(OrderStatus.ToBePaid.getStatus());
            orderCombo.setReplacementState(0);
            orderCombo.setCupboardState(0);
            orderCombo.setScannedStatus(0);
            orderCombo.setCreateTime(timestamp);
            orderCombo.setUpdateTime(timestamp);
            orderCombo.setGoodsType(GoodsTypeEnum.ENTITY.getCode());
            orderCombo.setCityName(machine.getCityName());

            List<String> packageIds = orderCreateDTO.getShoppingCartList().stream().map(ShoppingCartsDto::getPackageId).collect(Collectors.toList());
            Map<String, Combo> ComboMapById = comboService.listByPackageIds(packageIds).stream().collect(Collectors.toMap(Combo::getComboId, payload -> payload));
            Map<String, ImgResource> imgResourceMapById = imgResourceService.listByThumbIds(ComboMapById.values().stream().map(Combo::getThumb).collect(Collectors.toList())).stream().collect(Collectors.toMap(ImgResource::getImgId, payload -> payload));

            // 封装订单子表数据
            List<OrderDetail> orderDetails = orderCreateDTO.getShoppingCartList().stream().map(item -> {
                Combo combo = ComboMapById.getOrDefault(item.getPackageId(), new Combo());
                ImgResource imgResource = imgResourceMapById.getOrDefault(combo.getThumb(), new ImgResource());
                List<OrderDetail> orderDetailsTemp = CollUtil.newArrayList();
                for (int i = 0; i < item.getQuantity(); i++) {
                    OrderDetail orderDetail = new OrderDetail();
                    orderDetail.setOrderDetailId(String.valueOf(wFGIdGenerator.next()));
                    orderDetail.setOrderId(orderCombo.getOrderId());
                    orderDetail.setPackageId(item.getPackageId());
                    orderDetail.setPackageName(combo.getName());
                    orderDetail.setIsHeatAdditive(true);
                    orderDetail.setHeatTime(combo.getHeatTime().longValue());
                    orderDetail.setStatus(PackageStatusEnums.PendingPayment.getStatus());
                    orderDetail.setApplietAmount(combo.getSalePrice());
                    orderDetail.setDeviceAmount(combo.getSalePrice());
                    orderDetail.setAmount(combo.getSalePrice());
                    orderDetail.setThumbUrl(imgResource.getUrl());
                    orderDetail.setCompletedCount(0L);
                    orderDetail.setPickedUpCount(0L);
                    orderDetail.setCount(1L);
                    orderDetail.setScannedStatus(0);
                    orderDetail.setReplacementState(0);
                    orderDetail.setCreateTime(timestamp);
                    orderDetail.setUpdateTime(timestamp);
                    orderDetail.setCityName(machine.getCityName());
                    orderDetailsTemp.add(orderDetail);
                }
                return orderDetailsTemp;
            }).flatMap(Collection::stream).toList();
            // 统计小程序价格，优惠 前 价格,如果价格为0，则设置为0.01
            BigDecimal totalAppPrice = orderDetails.stream().map(item -> new BigDecimal(item.getApplietAmount())).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, BigDecimal.ROUND_UP);
            // 统计设备价格
            BigDecimal totalDevicePrice = orderDetails.stream().map(item -> new BigDecimal(item.getDeviceAmount())).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, BigDecimal.ROUND_UP);
            // 小程序价格如果为0，则设置为0.01
            if (NumberUtil.isLessOrEqual(totalAppPrice, BigDecimal.ZERO)) {
                totalAppPrice = new BigDecimal("0.01");
                OrderDetail first = CollUtil.getFirst(orderDetails);
                first.setAmount(totalAppPrice.toString());
            }

            // 设置价格，为用户实际支付的小程序的价格, 如果价格为0，则设置为0.01
            orderCombo.setPrice(totalAppPrice.toString());
            // 设置价格，总价为设备的价格
            orderCombo.setTotalPrice(totalDevicePrice);
            // 商品优惠价格。设备价格-小程序价格
            orderCombo.setAppDiscountMoney(NumberUtil.sub(totalDevicePrice, totalAppPrice));
            // 获取优惠券,计算优惠后的价格
            if (Objects.nonNull(orderCreateDTO.getClientCouponId())) {
                ClientCoupon clientCoupon = clientCouponService.findByIdAndClientId(orderCreateDTO.getClientCouponId(), clientId);
                Assert.isFalse(Objects.isNull(clientCoupon), "无此优惠券");
                List<ClientCoupon> availableCoupons = clientCouponService.getAvailableCoupons(orderCreateDTO, Collections.singletonList(clientCoupon));
                Assert.isFalse(CollUtil.isEmpty(availableCoupons), "此优惠券已不满足使用条件");

                ClientCouponBestDTO clientCouponBestDTO = this.settlementInfoOrder(orderCreateDTO);
                if (0 == clientCoupon.getCombo()) {
                    // 使用优惠券后，单个套餐的价格
                    BigDecimal finalTotalAppPrice = totalAppPrice;
                    orderDetails.stream().forEach(item -> {
                        BigDecimal amount = new BigDecimal(item.getApplietAmount());
                        BigDecimal discountMoney = clientCouponService.calculateDiscountMoney(amount, finalTotalAppPrice, clientCouponBestDTO.getDiscountMoney());
                        item.setAmount(discountMoney.toString());
                    });
                    BigDecimal bigDecimal = orderDetails.stream().map(item -> new BigDecimal(item.getAmount())).reduce(BigDecimal.ZERO, BigDecimal::add);
                    if (NumberUtil.isGreater(clientCouponBestDTO.getDiscountedAfterPrice(), bigDecimal)) {
                        BigDecimal sub = NumberUtil.sub(clientCouponBestDTO.getDiscountedAfterPrice(), bigDecimal).abs();
                        OrderDetail orderDetail = CollUtil.getFirst(orderDetails);
                        orderDetail.setAmount(NumberUtil.add(new BigDecimal(orderDetail.getAmount()), sub).toString());
                    }
                } else {
                    List<String> comboIds = marCouponComboMapper.selectByMarCouponIds(Collections.singletonList(clientCoupon.getCouponId())).stream().map(MarCouponCombo::getComboId).collect(Collectors.toList());
                    BigDecimal totalAppComboPrice = orderDetails.stream().filter(item -> comboIds.contains(item.getPackageId())).map(item -> new BigDecimal(item.getApplietAmount())).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, BigDecimal.ROUND_UP);
                    orderDetails.stream().filter(item -> comboIds.contains(item.getPackageId())).forEach(item -> {
                        BigDecimal amount = new BigDecimal(item.getApplietAmount());
                        BigDecimal discountMoney = clientCouponService.calculateDiscountMoney(amount, totalAppComboPrice, clientCouponBestDTO.getDiscountMoney());
                        item.setAmount(discountMoney.toString());
                    });
                    BigDecimal bigDecimal = orderDetails.stream().filter(item -> comboIds.contains(item.getPackageId())).map(item -> new BigDecimal(item.getAmount())).reduce(BigDecimal.ZERO, BigDecimal::add);
                    if (NumberUtil.isGreater(clientCouponBestDTO.getDiscountedAfterPrice(), bigDecimal)) {
                        BigDecimal sub = NumberUtil.sub(clientCouponBestDTO.getDiscountedAfterPrice(), bigDecimal).abs();
                        orderDetails.stream().filter(item -> comboIds.contains(item.getPackageId())).limit(1).forEach(item -> {
                            item.setAmount(NumberUtil.add(new BigDecimal(item.getAmount()), sub).toString());
                        });
                    }
                }

                // 设置优惠券信息，以及优惠后用户实际支付的价格
                orderCombo.setCouponId(clientCouponBestDTO.getCouponId());
                orderCombo.setClientCouponId(clientCouponBestDTO.getId());
                BigDecimal bigDecimal = orderDetails.stream().map(item -> new BigDecimal(item.getAmount())).reduce(BigDecimal.ZERO, BigDecimal::add);
                orderCombo.setPrice(bigDecimal.toString());
                // 优惠券优惠的金额
                orderCombo.setCouponDiscountMoney(clientCouponBestDTO.getDiscountMoney().setScale(2, BigDecimal.ROUND_DOWN));
            }

            // 封装orderList数据
            Client client = clientService.getById(clientId);
            Area area = areaService.getById(machine.getAreaId());
            Route route = routeService.getById(machine.getRouteId());
            Position position = positionService.getById(machine.getPositionId());
            Company company = companyService.getById(machine.getCompanyId());
            List<OrderList> orderLists = orderDetails.stream().map(item -> {
                Combo combo = ComboMapById.getOrDefault(item.getPackageId(), new Combo());
                OrderList orderList = new OrderList();
                orderList.setId(item.getOrderDetailId());
                orderList.setOrderSn(orderCombo.getOrderId());
                orderList.setOrderTime(timestamp);
                orderList.setUserName(client.getNickName());
                orderList.setUserMobile(client.getMobile());
                orderList.setAmt(item.getAmount().toString());
                orderList.setOriginalPrice(new BigDecimal(item.getApplietAmount()));
                // orderList.setPayType(PayTypeEnum.WeChatPay.getCode().toString());
                orderList.setPayStatus(PayStatusEnum.UNPAID.getCode());
                orderList.setComboId(combo.getComboId());
                orderList.setComboName(combo.getName());
                String tagIds = combo.getTagId();
                List<String> tagIdList = StrUtil.split(tagIds, ",");
                if (CollUtil.isNotEmpty(tagIdList)) {
                    String tagNames = tagComboService.listByIds(tagIdList).stream().map(TagCombo::getTagName).collect(Collectors.joining(","));
                    orderList.setComboTag(tagNames);
                }
                orderList.setMachineId(orderCombo.getMachineId());
                orderList.setCompanyId(machine.getCompanyId());
                orderList.setCompanyName(company.getName());
                orderList.setAreaId(machine.getAreaId());
                orderList.setAreaName(area.getName());
                orderList.setRouteId(machine.getRouteId());
                orderList.setRouteName(route.getName());
                orderList.setPositionId(machine.getPositionId());
                orderList.setPositionName(position.getPositonName());
                orderList.setPositionAddr(machine.getPositionAddr());
                orderList.setIsException("1");
                orderList.setOrderType(OrderTypeEnum.WeChat.getCode().toString());
                orderList.setUpdateTime(timestamp);
                orderList.setUpdateName(client.getNickName());
                orderList.setCount(1L);
                orderList.setGoodsType(GoodsTypeEnum.ENTITY.getCode());
                orderList.setCityName(machine.getCityName());
                return orderList;
            }).collect(Collectors.toList());

            // 修改优惠券状态为已使用
            clientCouponService.updateStatusById(orderCreateDTO.getClientCouponId(), ClientCouponStatusEnums.USED.getCode());
            // 更新库存
            deviceCurrentComboStockService.decreaseStockQuantity(orderCreateDTO.getShoppingCartList(), orderCreateDTO.getMachineId());
            // 更新订单数据
            orderComboService.save(orderCombo);
            orderDetailService.saveBatch(orderDetails);
            orderListService.saveBatch(orderLists);
            // 清除购物车数据
            // shoppingCartsService.deleteByUserIdAndMachineId(clientId, orderCreateDTO.getMachineId());
            // 将订单信息加入到延迟队列。如果超时没有支付，取消订单，恢复库存
            Gson gson = new Gson();
            String json = gson.toJson(orderCombo);
            redisDelayQueueUtil.addDelayQueue(json, 3, TimeUnit.MINUTES, CacheConstants.MINAPP_ORDER_CREATE_DELAY_QUEUE_KEY);

            // 封装返回结果
            PositionType positionType = positionTypeMapper.getSaleTimeByMachineId(orderCreateDTO.getMachineId());
            OrderCreateVO orderCreateVO = new OrderCreateVO();
            orderCreateVO.setOrderId(orderCombo.getOrderId());
            orderCreateVO.setStatus(OrderStatus.ToBePaid.getStatus());
            orderCreateVO.setExpirationTime(DateUtil.offset(timestamp, DateField.MINUTE, 3));
            orderCreateVO.setSaleEndTime(positionType.getSaleEndTime());
            orderCreateVO.setPaidPrice(new BigDecimal(orderCombo.getPrice()));
            orderCreateVO.setCouponId(orderCombo.getCouponId());
            return orderCreateVO;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 取消订单
     *
     * @param orderId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancelOrder(String orderId, String reason) {
        OrderCombo orderCombo = orderComboService.getById(orderId);
        if (orderCombo == null) {
            return false;
        }
        // 制作状态检测
        if (!StrUtil.equals(orderCombo.getStatus(), OrderStatus.ToBePaid.getStatus())) {
            return false;
        }

        // 微信关闭订单
        payService.closeOrder(orderId);
        redisService.deleteObject(CacheConstants.MINAPP_ORDER_CREATE_WX_PAY_PARTNER_JSAPI_KEY + orderId);

        // 修改订单状态
        Date date = new Date();
        orderCombo.setReason(reason);
        orderCombo.setStatus(OrderStatus.Canceled.getStatus());
        orderCombo.setUpdateTime(date);
        orderComboService.updateById(orderCombo);

        List<OrderList> orderLists = orderListService.listByOrderSn(orderId);
        orderLists.forEach(item -> {
            item.setPayStatus(PayStatusEnum.CANCELLED.getCode());
            item.setUpdateTime(date);
            item.setRefundReason(reason);
        });
        orderListService.updateBatchById(orderLists);

        // 修改库存锁,更新库存
        List<OrderDetail> orderDetails = orderDetailService.listByOrderId(orderId);
        deviceCurrentComboStockService.recoverStockQuantity(orderCombo, orderDetails);

        // 修改优惠券
        // LambdaUpdateWrapper<OrderCombo> orderComboUpdateWrapper = Wrappers.lambdaUpdate(OrderCombo.class);
        // orderComboUpdateWrapper.eq(OrderCombo::getOrderId, orderId);
        // orderComboUpdateWrapper.set(OrderCombo::getClientCouponId, null);
        // orderComboUpdateWrapper.set(OrderCombo::getCouponId, null);
        // orderComboService.update(orderComboUpdateWrapper);
        clientCouponService.updateStatusById(orderCombo.getClientCouponId(), ClientCouponStatusEnums.NORMAL.getCode());

        return true;
    }

    /**
     * 获取订单二维码
     *
     * @param orderDetailBtnDTO
     * @return
     */
    @Override
    public String getQRCode(OrderDetailBtnDTO orderDetailBtnDTO) {
        log.info("获取订单二维码:{}", orderDetailBtnDTO);
        OrderCombo orderCombo = orderComboService.getById(orderDetailBtnDTO.getOrderId());
        if (!StrUtil.equals(orderCombo.getStatus(), OrderStatus.AwaitPackage.getStatus())) {
            throw new ServiceException("请选择'制作完成'或者'等待制作'的套餐取餐");
        }
        String isPickUp = redisService.getCacheObject("makePackage" + orderDetailBtnDTO.getOrderId());
        List<OrderDetail> orderDetails;
        if (CollUtil.isEmpty(orderDetailBtnDTO.getOrderDetailId())) {
            orderDetails = orderDetailService.listByOrderId(orderCombo.getOrderId());
        } else {
            orderDetails = orderDetailService.listByOrderDetailIds(orderDetailBtnDTO.getOrderDetailId());
        }
        QRCodeDTO qrCodeDTO = new QRCodeDTO();
        // 如果为制作
        if (StringUtils.isEmpty(isPickUp)) {
            for (OrderDetail item : orderDetails) {
                if (item.getScannedStatus() == 1) {
                    throw new ServiceException("取餐码已使用");
                }
                Long madeCount = 0L;
                OrderMake orderMake = orderMakeService.listByOrderDetailId(item.getOrderDetailId());
                if (orderMake != null) {
                    madeCount = orderMake.getMakeCount();
                }
                if (madeCount + item.getCount() > item.getCount()) {
                    log.error("小程序获取二维码失败,原因需制作数量大于可制作数量,请求信息:{}", item);
                    return null;
                }
                qrCodeDTO.setOperateType("make");
            }
        }// 如果为取餐
        else {
            // 判断制作完成数量是否足够取餐
            for (OrderDetail item : orderDetails) {
                Long availableCount = item.getCompletedCount() - item.getPickedUpCount();
                if (item.getCount() > availableCount) {
                    log.error("小程序获取取餐码失败,原因需取餐数量大于制作完成数量,请求信息:{}", item);
                    return null;
                }
            }
            qrCodeDTO.setOperateType("pickup");
        }
        String QRCodeId = String.valueOf(wFGIdGenerator.next());
        // 设置二维码过期时间
        Calendar ca = Calendar.getInstance();
        // 失效的时间
        ca.set(Calendar.HOUR_OF_DAY, 23);
        ca.set(Calendar.MINUTE, 59);
        ca.set(Calendar.SECOND, 59);
        long fl = ca.getTimeInMillis();
        long seconds = (fl - System.currentTimeMillis()) / 1000;
        qrCodeDTO.setOrderId(orderCombo.getOrderId());
        qrCodeDTO.setDetailList(orderDetails);

        // "order:take:qr:code"+QRCodeId
        redisService.setCacheObject(MINAPP_ORDER_TAKE_QR_CODE_KEY + QRCodeId, qrCodeDTO, seconds, TimeUnit.SECONDS);
        log.info("小程序用户获取二维码成功,订单信息:{},二维码id:{}", orderCombo.getOrderId(), QRCodeId);
        Map<String, Object> resultMap = new HashMap<>();
        if (orderCombo.getGoodsType() == 3) {
            // 订单商品返回组装二维码参数
            resultMap.put("qrCodeId", QRCodeId);
            resultMap.put("goodType", orderCombo.getGoodsType());
            return JSONObject.toJSONString(resultMap);
        }
        return QRCodeId;
    }

    /**
     * 订单退款
     *
     * @param orderId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean refundOrder(String orderId, String refundReason) {
        OrderCombo orderCombo = orderComboService.getById(orderId);
        if (orderCombo == null) {
            return false;
        }
        // 制作状态检测
        if (!StrUtil.equals(orderCombo.getStatus(), OrderStatus.AwaitPackage.getStatus())) {
            return false;
        }
        List<OrderDetail> orderDetails = orderDetailService.listByOrderId(orderId);
        long count = orderDetails.stream().filter(item -> !StrUtil.equals(item.getStatus(), PackageStatusEnums.WaitingToBeMade.getStatus())).count();
        if (count > 0) {
            log.error("小程序订单退款失败,原因订单中有已有套餐开始制作，无法取消:{}", orderId);
            return false;
        }
        // 超时检测
        List<OrderList> orderLists = orderListService.listByOrderSn(orderId);
        Date date = new Date();
        long count1 = orderLists.stream().filter(item -> {
            long between = DateUtil.between(item.getPayTime(), date, DateUnit.MINUTE);
            return between > 3;
        }).count();
        if (count1 > 0) {
            log.error("小程序订单退款失败,原因订单中有套餐已超时,无法取消:{}", orderId);
            return false;
        }

        // 修改订单状态
        orderCombo.setStatus(OrderStatus.APPLY_REFUND.getStatus());
        orderCombo.setUpdateTime(date);
        orderComboService.updateById(orderCombo);
        orderDetails.forEach(item -> {
            item.setStatus(PackageStatusEnums.APPLY_REFUND.getStatus());
            item.setUpdateTime(date);
        });
        orderDetailService.updateBatchById(orderDetails);

        orderLists.forEach(item -> {
            item.setPayStatus(PayStatusEnum.REFUNDING.getCode());
            item.setUpdateTime(date);
            item.setRefundReason(refundReason);
        });
        orderListService.updateBatchById(orderLists);
        // 修改库存锁,更新库存
        deviceCurrentComboStockService.recoverStockQuantity(orderCombo, orderDetails);

        // 修改优惠券
        clientCouponService.updateStatusById(orderCombo.getClientCouponId(), ClientCouponStatusEnums.NORMAL.getCode());

        OrderList orderList = CollUtil.getFirst(orderLists);
        if (PayTypeEnum.WeChatPay.getCode().toString().equals(orderList.getPayType())) {
            Boolean refund = payService.refund(orderCombo);
            if (!refund) {
                log.error("小程序订单退款失败,订单号:{}", orderId);
            }
        }

        // 如果是霸王优惠券 or 余额支付，模拟微信退款回调
        Boolean isBabyCouponOrder = this.isBabyCouponOrder(orderCombo.getCouponId());
        if (isBabyCouponOrder || PayTypeEnum.BalancePay.getCode().toString().equals(orderList.getPayType())) {
            this.simulateWechatPayOrderRefundNotify(orderCombo);
        }

        // 如果是余额支付，更新 client，client_bill
        if (PayTypeEnum.BalancePay.getCode().toString().equals(orderList.getPayType())) {
            Client client = clientService.balanceRefundOrder(orderCombo);
            clientBillService.balanceRefundOrder(orderCombo, client);
        }

        return true;
    }

    @Override
    public void orderCreateNotify(WxPayPartnerNotifyV3Result wxPayPartnerNotifyV3Result) {
        String orderId = wxPayPartnerNotifyV3Result.getResult().getOutTradeNo();

        RedisDistributedLock redisDistributedLock = new RedisDistributedLock(redisTemplate, "client:wxpay:orderCreateNotify:" + orderId, 10);
        try {
            // 加锁
            redisDistributedLock.lock();
            OrderCombo orderCombo = orderComboService.getById(orderId);

            if (OrderStatus.AwaitPackage.getStatus().equals(orderCombo.getStatus())) {
                return;
            }
            List<OrderList> orderLists = orderListService.listByOrderSn(orderId);
            OrderList first = CollUtil.getFirst(orderLists);
            if (!PayStatusEnum.UNPAID.getCode().equals(first.getPayStatus())) {
                return;
            }

            List<OrderDetail> orderDetails = orderDetailService.listByOrderId(orderId);
            Client client = clientService.getById(orderCombo.getUserId());

            Date date = new Date();
            // 修改订单状态
            orderCombo.setStatus(OrderStatus.AwaitPackage.getStatus());
            orderCombo.setUpdateTime(date);

            orderDetails.forEach(item -> {
                item.setStatus(PackageStatusEnums.WaitingToBeMade.getStatus());
                item.setUpdateTime(date);
            });

            orderLists.forEach(item -> {
                item.setPayType(PayTypeEnum.WeChatPay.getCode().toString());
                item.setPayTradeNo(wxPayPartnerNotifyV3Result.getResult().getTransactionId());
                item.setPayStatus(PayStatusEnum.PAID.getCode());
                item.setPayTime(DateUtil.parseUTC(wxPayPartnerNotifyV3Result.getResult().getSuccessTime()).toTimestamp());
                item.setUpdateTime(date);
            });

            // 事物提交
            Boolean execute = transactionTemplate.execute(e -> {
                orderComboService.updateById(orderCombo);
                orderDetailService.updateBatchById(orderDetails);
                orderListService.updateBatchById(orderLists);
                return Boolean.TRUE;
            });
            if (!execute) {
                log.error("订单支付成功,订单号:{},修改订单状态失败", orderId);
                return;
            }
            log.info("订单支付成功,订单号:{},修改订单状态成功", orderId);
            // 通知设备支付状态
            machineService.notifyDevicePayStatus(orderCombo, orderDetails, orderLists);
            remoteOperationService.orderRouting(orderId, PayTypeEnum.WeChatPay.getCode());
        } catch (Exception e) {
            log.info("orderCreateNotify:出现异常， 异常信息：{}", e.getMessage());
        } finally {
            redisDistributedLock.unlock();
        }
    }

    @Override
    public void orderCreateBalanceNotify(BalancePayDTO balancePayDTO, StoredCalculateResultDTO storedResultsDTO) {
        String orderId = balancePayDTO.getOrderId();

        OrderCombo orderCombo = orderComboService.getById(orderId);

        if (OrderStatus.AwaitPackage.getStatus().equals(orderCombo.getStatus())) {
            return;
        }
        List<OrderList> orderLists = orderListService.listByOrderSn(orderId);
        OrderList first = CollUtil.getFirst(orderLists);
        if (!PayStatusEnum.UNPAID.getCode().equals(first.getPayStatus())) {
            return;
        }

        List<OrderDetail> orderDetails = orderDetailService.listByOrderId(orderId);
        Client client = clientService.getById(orderCombo.getUserId());

        Date date = new Date();
        // 修改订单状态
        orderCombo.setStatus(OrderStatus.AwaitPackage.getStatus());
        orderCombo.setUpdateTime(date);
        orderCombo.setGiftGold(storedResultsDTO.getStoredActualResultsDTO().getGiftMoney());

        orderDetails.forEach(item -> {
            item.setStatus(PackageStatusEnums.WaitingToBeMade.getStatus());
            item.setUpdateTime(date);
        });

        Map<String, List<OrderGiftGold>> storedResultsDTOMap = storedResultsDTO.getOrderGiftGolds().stream().collect(Collectors.groupingBy(OrderGiftGold::getOrderListId));
        orderLists.forEach(item -> {
            item.setPayType(PayTypeEnum.BalancePay.getCode().toString());
            item.setPayStatus(PayStatusEnum.PAID.getCode());
            item.setPayTime(DateUtil.date(date).toTimestamp());
            item.setUpdateTime(date);
            List<OrderGiftGold> orderGiftGolds = storedResultsDTOMap.getOrDefault(item.getId(), Collections.emptyList());
            item.setGiftGold(orderGiftGolds.stream().map(OrderGiftGold::getGiftGold).reduce(BigDecimal.ZERO, BigDecimal::add));
        });

        orderComboService.updateById(orderCombo);
        orderDetailService.updateBatchById(orderDetails);
        orderListService.updateBatchById(orderLists);
        log.info("订单支付成功,订单号:{},修改订单状态成功", orderId);
    }

    /**
     * 微信支付退款回调
     *
     * @param wxPayPartnerRefundNotifyV3Result
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderRefundNotify(WxPayPartnerRefundNotifyV3Result wxPayPartnerRefundNotifyV3Result) {
        String orderId = wxPayPartnerRefundNotifyV3Result.getResult().getOutTradeNo();
        String orderDetailId = wxPayPartnerRefundNotifyV3Result.getResult().getOutRefundNo();

        OrderCombo orderCombo = orderComboService.getById(orderId);
        List<OrderDetail> orderDetails;
        List<OrderList> orderLists;

        // orderId 与 orderDetailId 相同为整单退款。不同为部分退款。
        if (StrUtil.equals(orderId, orderDetailId)) {
            orderDetails = orderDetailService.listByOrderId(orderId);
            orderLists = orderListService.listByOrderSn(orderId);
        } else {
            orderDetails = CollUtil.newArrayList(orderDetailService.listByOrderDetailId(orderDetailId));
            orderLists = CollUtil.newArrayList(orderListService.listByOrderDetailId(orderDetailId));
        }

        // 如果存在退款状态订单，则返回（防止微信重复调用）
        List<OrderList> refundList = orderLists.stream().filter(orderList -> orderList.getPayStatus().equals(PayStatusEnum.REFUNDED.getCode())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(refundList)) {
            return;
        }

        Date date = new Date();
        // 修改订单状态
        orderCombo.setStatus(OrderStatus.Refund.getStatus());
        orderCombo.setUpdateTime(date);

        orderDetails.forEach(item -> {
            item.setStatus(PackageStatusEnums.Refunded.getStatus());
            item.setUpdateTime(date);
        });

        orderLists.forEach(item -> {
            item.setRefundSn(wxPayPartnerRefundNotifyV3Result.getResult().getRefundId());
            item.setRefundTime(DateUtil.parseUTC(wxPayPartnerRefundNotifyV3Result.getResult().getSuccessTime()).toTimestamp());
            item.setPayTradeNo(wxPayPartnerRefundNotifyV3Result.getResult().getTransactionId());
            item.setPayStatus(PayStatusEnum.REFUNDED.getCode());
            item.setUpdateTime(date);
        });

        // orderId 与 orderDetailId 相同为整单退款。不同为部分退款。
        if (StrUtil.equals(orderId, orderDetailId)) {
            orderLists.forEach(item -> {
                item.setRefundAmt(item.getAmt());
            });
        } else {
            orderLists.forEach(item -> {
                BigDecimal payerRefund = NumberUtil.toBigDecimal(wxPayPartnerRefundNotifyV3Result.getResult().getAmount().getPayerRefund());
                item.setRefundAmt(NumberUtil.div(payerRefund, NumberUtil.toBigDecimal("100")).setScale(2).toString());
            });
        }

        orderComboService.updateById(orderCombo);
        orderDetailService.updateBatchById(orderDetails);
        orderListService.updateBatchById(orderLists);
        BigDecimal payerRefund = NumberUtil.toBigDecimal(wxPayPartnerRefundNotifyV3Result.getResult().getAmount().getPayerRefund());
        remoteOperationService.orderReimburse(orderId, NumberUtil.div(payerRefund, NumberUtil.toBigDecimal("100")).setScale(2));
        //
        // // 扣除积分
        // ClientIntegral clientIntegral = clientIntegralService.findByOrderId(orderId);
        // Client client = clientService.getById(orderCombo.getUserId());
        // if (Objects.nonNull(clientIntegral) && clientIntegral.getStatus().equals(ClientIntegralStatusEnum.NORMAL.getStatus()) && Objects.nonNull(client) && Objects.nonNull(clientIntegral.getIntegral()) && Objects.nonNull(client.getIntegral())) {
        //     clientIntegralService.editStatusByOrderId(orderId, ClientIntegralStatusEnum.REFUND.getStatus());
        //     client.setIntegral(client.getIntegral() - clientIntegral.getIntegral());
        //     clientService.edit(client);
        // }
    }

    /**
     * 订单操作-客服代取
     *
     * @param orderDetailBtnDTO
     * @return
     */
    @Override
    // @Transactional(rollbackFor = Exception.class)
    public Boolean operationPickup(OrderDetailBtnDTO orderDetailBtnDTO) {
        log.info("用户小程序代取,订单id:{}", orderDetailBtnDTO);
        RedisDistributedLock lock = new RedisDistributedLock(redisTemplate, CacheConstants.OPERATION_PICKUP_ORDER_LOCK_KEY + orderDetailBtnDTO.getOrderId(), 10);
        try {
            // 加锁
            lock.lock();
            Boolean execute = transactionTemplate.execute(e -> {
                OrderCombo orderCombo = orderComboService.getById(orderDetailBtnDTO.getOrderId());
                if (!StrUtil.equals(orderCombo.getStatus(), OrderStatus.AwaitPackage.getStatus())) {
                    lock.unlock();
                    throw new RuntimeException("当前订单已完成");
                }

                List<OrderDetail> orderDetails;
                if (CollUtil.isNotEmpty(orderDetailBtnDTO.getOrderDetailId())) {
                    orderDetails = orderDetailService.listByOrderDetailIds(orderDetailBtnDTO.getOrderDetailId());
                } else {
                    orderDetails = orderDetailService.listByOrderId(orderDetailBtnDTO.getOrderId());
                }

                // 数据校验
                for (OrderDetail orderDetail : orderDetails) {
                    String status = orderDetail.getStatus();
                    if (status.equals(PackageStatusEnums.Refunded.getStatus())) {
                        lock.unlock();
                        throw new RuntimeException("该订单已退款");
                    }
                    if (status.equals(PackageStatusEnums.InTheMaking.getStatus())) {
                        lock.unlock();
                        throw new RuntimeException("订单制作中");
                    }
                }
                for (OrderDetail orderDetail : orderDetails) {
                    Long madeCount = 0L;
                    OrderMake orderMake = orderMakeService.listByOrderDetailId(orderDetail.getOrderDetailId());
                    if (orderMake != null) {
                        madeCount = orderMake.getMakeCount();
                    }
                    OrderDetail itemList = orderDetailService.listByOrderDetailId(orderDetail.getOrderDetailId());
                    if (madeCount + orderDetail.getCount() > itemList.getCount()) {
                        lock.unlock();
                        throw new RuntimeException("需制作数量大于可制作数量");
                    }
                }

                // 更新订单状态
                orderCombo.setReplacementState(1);
                orderComboService.updateById(orderCombo);
                String pickupNum = "";
                Integer num = 1;
                for (OrderDetail orderDetail : orderDetails) {
                    String mobile = orderListMapper.getByOrderId(orderDetail.getOrderId());
                    pickupNum = mobile.substring(7, 11);

                    // 组装取餐码
                    if (num < 10) {
                        pickupNum = pickupNum + "-0" + num;
                    } else {
                        pickupNum = pickupNum + "-" + num;
                    }
                    num++;
                    // 修改详情状态
                    orderDetail.setOrderDetailId(orderDetail.getOrderDetailId());
                    orderDetail.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                    orderDetail.setStatus(PackageStatusEnums.WaitingToBeMade.getStatus());
//                    orderDetail.setQueueTime(new Timestamp(System.currentTimeMillis()));
                    orderDetail.setReplacementState(1);
                    orderDetail.setPickupNum(pickupNum);
                    log.info("修改订单的状态,订单id:{},订单详情id:{},状态:{}", orderDetail.getOrderId(), orderDetail.getOrderDetailId(), orderDetail.getStatus());
                    orderDetailService.updateById(orderDetail);
                    orderDetailService.setQueueTime(orderDetail.getOrderId());
                    // 修改或添加订单套餐制作
                    OrderMake make = orderMakeService.listByOrderDetailId(orderDetail.getOrderDetailId());
                    Date date = new Date();
                    if (make == null) {
                        OrderMake orderMake = new OrderMake();
                        orderMake.setOrderDetailId(orderDetail.getOrderDetailId());
                        orderMake.setOrderMakeId(String.valueOf(wFGIdGenerator.next()));
                        orderMake.setOrderId(orderDetailBtnDTO.getOrderId());
                        orderMake.setMakeCount(orderDetail.getCount());
                        orderMake.setCreateTime(date);
                        orderMake.setUpdateTime(date);
                        orderMakeService.updateById(orderMake);
                    } else {
                        Long makeCount = make.getMakeCount();
                        make.setMakeCount(makeCount + orderDetail.getCount().intValue());
                        make.setUpdateTime(date);
                        orderMakeService.updateById(make);
                    }
                }

                R<Boolean> booleanR = remoteOperationService.orderMinMake(orderDetailBtnDTO.getOrderId());
                if (booleanR.getCode() != 200) {
                    lock.unlock();
                    throw new RuntimeException(booleanR.getMsg());
                }

                return Boolean.TRUE;
            });
            return execute;
        } catch (Exception e) {
            log.info("出现异常， 异常信息：{}", e.getMessage());
            return Boolean.FALSE;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 订单操作-立即制作
     *
     * @param orderDetailBtnDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response<?> makePackage(OrderDetailBtnDTO orderDetailBtnDTO) {

        String orderId = orderDetailBtnDTO.getOrderId();
        // 查看订单状态
        List<OrderList> orderList = orderListService.listByOrderSnAndStatus(orderId, PayStatusEnum.PAID.getCode());
        if (CollUtil.isEmpty(orderList)) {
            log.info("订单未支付");
            return Response.failuer("订单未支付");
        }

        // 查询订单是否有已制作套餐
        long count = 0;
        // 批量查询详情列表
        List<OrderDetail> detailList = orderDetailService.listByOrderId(orderId);
        // 判断订单状态
        for (OrderDetail orderDetail : detailList) {
            count += orderDetail.getCount();
            if (!orderDetail.getStatus().equals(PackageStatusEnums.WaitingToBeMade.getStatus())) {
                log.info("已有选中的套餐正在制作");
                return Response.failuer("已有选中的套餐正在制作");
            }
        }

        OrderCombo orderCombo = orderComboService.getById(orderId);
        String machineId = orderCombo.getMachineId();
        RLock lock = redissonClient.getLock("makePackage" + machineId);
        lock.lock();

        // 获取赞存柜信息
        String key = "temporaryLockers" + machineId;
        String lockerCount = redisTemplate.opsForValue().get(key);
        if (lockerCount == null || StringUtils.isEmpty(lockerCount)) {
            lock.unlock();
            log.info("无该设备暂存柜信息");
            return Response.failuer("无该设备暂存柜信息");
        }
        // 校验赞存柜数量
        long actualCount = Long.parseLong(lockerCount);
        if (actualCount < count) {
            lock.unlock();
            log.info("暂存柜已满，建议去机器扫码取餐");
            return Response.failuer("暂存柜已满，建议去机器扫码取餐");
        }
        // 推送剩余赞存柜数量
        lockerCount = actualCount - count + "";
        redisTemplate.opsForValue().set(key, lockerCount);


        MakePackageDTO makePackageDTO = new MakePackageDTO();
        // 获取订单支付时间
//        Date payTime = orderList.get(0).getPayTime();
        // 支付时间
//        makePackageDTO.setPayTime(payTime.getTime());
        makePackageDTO.setOrderId(orderId);
        makePackageDTO.setPickupGoodsType("2TS");


        String userId = orderCombo.getUserId();
        Client client = clientService.getById(userId);
        makePackageDTO.setUserHeadUrl(client.getHeadImg());
        makePackageDTO.setPickupNum(client.getMobile().substring(7, 11));

        MqttDTO<MakePackageDTO> orderMakeDTOMqttDTO = new MqttDTO<>();
        orderMakeDTOMqttDTO.setCmd(MqttConstants.MAKEPACKAGE);
        orderMakeDTOMqttDTO.setPayload(makePackageDTO);

        /**
         * 主题是设备id
         */
        log.info("存柜:{}", JSONUtil.toJsonStr(orderMakeDTOMqttDTO));
        mqttGateway.sendToMqtt(machineId, JSONUtil.toJsonStr(orderMakeDTOMqttDTO));
        orderComboService.updateCupboardState(orderId);
        redisTemplate.opsForValue().set("makePackage" + orderId, "true", 1, TimeUnit.DAYS);
        lock.unlock();
        return Response.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkAllOrderExpired() {
        log.info("检查订单是否过期");
        List<OrderCombo> unfinishedOrder = orderComboMapper.getUnfinishedOrder(OrderStatus.AwaitPackage.getStatus(), null);
        List<String> machinedIds = unfinishedOrder.stream().map(OrderCombo::getMachineId).collect(Collectors.toList());
        if (CollUtil.isEmpty(machinedIds)) {
            return;
        }
        Map<String, DeviceInfoVO> machineMapById = machineMapper.getByIds(machinedIds).stream().collect(Collectors.toMap(DeviceInfoVO::getMachineId, Function.identity()));

        this.extracted(unfinishedOrder, machineMapById);
        // 更新前一天order_detail_wait 状态为0 的数据为 2
        orderDetailWaitService.updateStatus0To2ForPreviousDay();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkUserOrderExpired() {
        String clientId = SecurityUtils.getClientId();
        List<OrderCombo> unfinishedOrder = orderComboMapper.getUnfinishedOrder(OrderStatus.AwaitPackage.getStatus(), clientId);
        List<String> machinedIds = unfinishedOrder.stream().map(OrderCombo::getMachineId).collect(Collectors.toList());
        if (CollUtil.isEmpty(machinedIds)) {
            return;
        }
        Map<String, DeviceInfoVO> machineMapById = machineMapper.getByIds(machinedIds).stream().collect(Collectors.toMap(DeviceInfoVO::getMachineId, Function.identity()));

        this.extracted(unfinishedOrder, machineMapById);
    }

    @Override
    public List<OrderCombo> listByUserIdAndStatus(OrderListDTO orderListDTO) {
        // 根据用户id获取数据
        List<OrderCombo> orderComboList = orderComboService.listByUserIdAndStatus(orderListDTO);
        return orderComboList;
    }

    @Override
    public Integer getAwaitNum(String machineId) {
        List<OrderDetailWait> awaitNumInfo = orderDetailWaitMapper.getAwaitNumInfo(machineId);
        // 如果出口口为空，设置默认值A
        awaitNumInfo.stream().filter(item -> Objects.isNull(item.getOutMeal())).forEach(item -> item.setOutMeal("A"));
        // 根据出餐口分组，统计数量，出餐口字段不区分大小写
        Map<String, List<OrderDetailWait>> awaitNumInfoByOutMeal = awaitNumInfo.stream().collect(Collectors.groupingBy(item -> item.getOutMeal().toUpperCase()));

        // 当双出口的机器只有一个出餐口有排队信息时，直接返回0
        Integer type = Integer.valueOf(machineId.substring(7, 8));
        switch (type) {
            case 3 :
                if (awaitNumInfoByOutMeal.size() == 1) {
                    return 0;
                }
                break;
            case 4 :
                if (awaitNumInfoByOutMeal.size() == 1) {
                    return 0;
                }
                break;
            case 5 :
                if (awaitNumInfoByOutMeal.size() == 1) {
                    return 0;
                }
                break;
        }


        // 找出出餐口数量最小的
        List<OrderDetailWait> minOutMealList = awaitNumInfoByOutMeal.values().stream().min(Comparator.comparing(List::size)).orElse(new ArrayList<>());
        return minOutMealList.size();
    }

    @Override
    public OrderCreateVO simulateWechatPay(OrderCreateVO order) {
        Long couponId = order.getCouponId();
        if (Objects.isNull(couponId)) {
            return order;
        }
        MarCoupon marCoupon = marCouponService.findById(couponId);
        order.setCouponType(marCoupon.getType());
        if (!CouponTypeEnum.BABY_CUSTOMER.getCode().equals(marCoupon.getType())) {
            return order;
        }
        // 如果是霸王免单券，模拟微信订单支付
        WxPayPartnerNotifyV3Result.DecryptNotifyResult decryptNotifyResult = new WxPayPartnerNotifyV3Result.DecryptNotifyResult();
        decryptNotifyResult.setOutTradeNo(order.getOrderId());
        decryptNotifyResult.setSuccessTime(DateUtil.format(DateUtil.date(), DatePattern.UTC_WITH_XXX_OFFSET_PATTERN));
        WxPayPartnerNotifyV3Result wxPayPartnerNotifyV3Result = new WxPayPartnerNotifyV3Result();
        wxPayPartnerNotifyV3Result.setResult(decryptNotifyResult);

        this.orderCreateNotify(wxPayPartnerNotifyV3Result);
        return order;
    }

    @Override
    public Boolean isBabyCouponOrder(Long couponId) {
        if (Objects.isNull(couponId)) {
            return false;
        }
        MarCoupon marCoupon = marCouponService.findById(couponId);
        return CouponTypeEnum.BABY_CUSTOMER.getCode().equals(marCoupon.getType());
    }

    @Override
    public void simulateWechatPayOrderRefundNotify(OrderCombo orderCombo) {
        WxPayPartnerRefundNotifyV3Result wxPayPartnerRefundNotifyV3Result = new WxPayPartnerRefundNotifyV3Result();
        WxPayPartnerRefundNotifyV3Result.DecryptNotifyResult decryptNotifyResult = new WxPayPartnerRefundNotifyV3Result.DecryptNotifyResult();
        WxPayPartnerRefundNotifyV3Result.Amount amount = new WxPayPartnerRefundNotifyV3Result.Amount();
        decryptNotifyResult.setOutTradeNo(orderCombo.getOrderId());
        decryptNotifyResult.setOutRefundNo(orderCombo.getOrderId());
        decryptNotifyResult.setRefundId(null);
        amount.setPayerRefund(NumberUtil.mul(orderCombo.getPrice(), "100").intValue());
        decryptNotifyResult.setAmount(amount);
        decryptNotifyResult.setSuccessTime(DateUtil.format(DateUtil.date(), DatePattern.UTC_WITH_XXX_OFFSET_PATTERN));
        wxPayPartnerRefundNotifyV3Result.setResult(decryptNotifyResult);
        this.orderRefundNotify(wxPayPartnerRefundNotifyV3Result);
    }

    @Override
    public void balancePayOrderRefundNotify(OrderList orderList, BigDecimal amt) {
        WxPayPartnerRefundNotifyV3Result wxPayPartnerRefundNotifyV3Result = new WxPayPartnerRefundNotifyV3Result();
        WxPayPartnerRefundNotifyV3Result.DecryptNotifyResult decryptNotifyResult = new WxPayPartnerRefundNotifyV3Result.DecryptNotifyResult();
        WxPayPartnerRefundNotifyV3Result.Amount amount = new WxPayPartnerRefundNotifyV3Result.Amount();
        decryptNotifyResult.setOutTradeNo(orderList.getOrderSn());
        decryptNotifyResult.setOutRefundNo(orderList.getId());
        decryptNotifyResult.setRefundId(null);
        amount.setPayerRefund(NumberUtil.mul(amt, NumberUtil.toBigDecimal("100")).intValue());
        decryptNotifyResult.setAmount(amount);
        decryptNotifyResult.setSuccessTime(DateUtil.format(DateUtil.date(), DatePattern.UTC_WITH_XXX_OFFSET_PATTERN));
        wxPayPartnerRefundNotifyV3Result.setResult(decryptNotifyResult);
        this.orderRefundNotify(wxPayPartnerRefundNotifyV3Result);
    }

    @Override
    public DetailVO remindOrderStatus() {
        String clientId = SecurityUtils.getClientId();
        List<String> statusList = Arrays.asList(OrderStatus.AwaitPackage.getStatus(), OrderStatus.Finish.getStatus());
        OrderCombo orderCombo = orderComboService.listLastByClientId(clientId, statusList);
        if (Objects.isNull(orderCombo)) {
            return null;
        }

        // 生产完成1h后，首页卡片消失
        if (OrderStatus.Finish.getStatus().equals(orderCombo.getStatus())) {
            DateTime date = DateUtil.date();
            boolean isInOneHour = DateUtil.isIn(orderCombo.getUpdateTime(), DateUtil.offsetHour(date, -1), date);
            if (!isInOneHour) {
                return null;
            }
        }
        return this.detail(orderCombo.getOrderId(), "1", "1");
    }

    @Override
    public Boolean guidancePopup() {
        String clientId = SecurityUtils.getClientId();
        // 该功能上线后 不管新老用户 前三次下单都弹出该弹窗
        // 日期需要配置为正式上线时候的日期
        DateTime dateTime = DateTime.of("2024-10-23", "yyyy-MM-dd");
        Integer count = orderComboService.countOrderByClientIdAndCreateTime(clientId, dateTime);
        return 3 >= count;
    }

    @Override
    public Boolean isFirstOrder() {
        String clientId = SecurityUtils.getClientId();
        return orderComboService.isFirstOrder(clientId);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void balancePay(BalancePayDTO balancePayDTO) {
        // 计算扣款比例并扣款
        StoredCalculateResultDTO storedResultsDTO = payService.calculateBalancePay(balancePayDTO);
        // 余额支付回调
        this.orderCreateBalanceNotify(balancePayDTO, storedResultsDTO);
    }

    @Override
    public void balancePayNext(BalancePayDTO balancePayDTO) {
        String orderId = balancePayDTO.getOrderId();

        OrderCombo orderCombo = orderComboService.getById(orderId);
        List<OrderList> orderLists = orderListService.listByOrderSn(orderId);
        List<OrderDetail> orderDetails = orderDetailService.listByOrderId(orderId);

        // 通知设备支付状态
        machineService.notifyDevicePayStatus(orderCombo, orderDetails, orderLists);
        remoteOperationService.orderRouting(orderId, PayTypeEnum.BalancePay.getCode());
    }

    /**
     * 根据客户id获取订单号
     *
     * @param clientId
     * @return
     */
    @Override
    public List<String> orderIdList(String clientId) {
        OrderCombo orderCombo = new OrderCombo();
        orderCombo.setUserId(clientId);
        return orderComboMapper.getOrderIdList(orderCombo);
    }

    private void extracted(List<OrderCombo> unfinishedOrder, Map<String, DeviceInfoVO> machineMapById) {
        unfinishedOrder.forEach(orderCombo -> {
            DeviceInfoVO deviceInfoVO = machineMapById.getOrDefault(orderCombo.getMachineId(), null);
            if (Objects.isNull(deviceInfoVO)) {
                log.info("机器不存在，订单号：{}", orderCombo.getOrderId());
                return;
            }
            DateTime newDate = DateUtil.date();
            // 判断是否在营业时间内
            DateTime createTime = DateUtil.date(orderCombo.getCreateTime());

            Calendar saleBeginTime = DateUtil.parse(deviceInfoVO.getSaleBeginTime()).toCalendar();
            saleBeginTime.set(createTime.year(), createTime.month(), createTime.dayOfMonth());

            Calendar saleEndTime = DateUtil.parse(deviceInfoVO.getSaleEndTime()).toCalendar();
            saleEndTime.set(createTime.year(), createTime.month(), createTime.dayOfMonth());

            boolean in = DateUtil.isIn(newDate, DateUtil.date(saleBeginTime), DateUtil.date(saleEndTime));
            if (!in) {

                Client client = clientService.getById(orderCombo.getUserId());
                RLock lock = redissonClient.getLock(CacheConstants.CLIENT_INTEGRAL_LOCK_KEY + client.getId());

                try {
                    boolean b = lock.tryLock(5, TimeUnit.SECONDS);
                    Assert.isTrue(b, "稍后重试");
                    // 更新订单状态，修改用户超过营业时间待取餐订单
                    orderCombo.setStatus(OrderStatus.Finish.getStatus());
                    orderCombo.setUpdateTime(newDate);
                    orderCombo.setReason("超过营业时间未取餐");
                    orderComboService.updateById(orderCombo);

                    String enterpriseOrderSn = enterpriseOrderClientMapper.getEnterpriseOrderSn(orderCombo.getOrderId());
                    enterpriseOrderService.updateFinishTime(enterpriseOrderSn);

                    List<OrderDetail> orderDetails = orderDetailService.listByOrderId(orderCombo.getOrderId());
                    orderDetails.forEach(item -> {
                        item.setStatus(PackageStatusEnums.PickupIsComplete.getStatus());
                        item.setUpdateTime(newDate);
                    });
                    orderDetailService.updateBatchById(orderDetails);

                    List<OrderList> orderLists = orderListService.listByOrderSn(orderCombo.getOrderId());
                    orderLists.forEach(item -> {
                        item.setPayStatus(PayStatusEnum.COMPLETED.getCode());
                        item.setUpdateTime(newDate);
                        item.setRemark("超过营业时间未取餐");
                    });
                    orderListService.updateBatchById(orderLists);

                    // 添加积分
                    if (Objects.nonNull(client)) {
                        ClientIntegral clientIntegral = new ClientIntegral();
                        clientIntegral.setClientId(orderCombo.getUserId());
                        clientIntegral.setIntegral(new BigDecimal(orderCombo.getPrice()).intValue());
                        clientIntegral.setSource(1);
                        clientIntegral.setOrderId(orderCombo.getOrderId());
                        clientIntegral.setStatus(1);
                        clientIntegral.setType(1);
                        clientIntegral.setSurplusIntegral(client.getIntegral() + clientIntegral.getIntegral());
                        clientIntegralService.insert(clientIntegral);
                        client.setIntegral(null == client.getIntegral() ? clientIntegral.getIntegral() : client.getIntegral() + clientIntegral.getIntegral());
                        clientService.edit(client);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        });
    }
}
